package org.bookie.framework.services.impl;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.engine.ExternalServiceParameter;
import org.apache.tapestry.engine.IEngineService;
import org.apache.tapestry.engine.ILink;
import org.apache.tapestry.util.ContentType;
import org.bookie.model.Category;
import org.bookie.model.Event;
import org.bookie.model.services.CategoryDAO;
import org.bookie.model.services.EventDAO;

import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedOutput;

/**
 * Service that emmits RSS feeds based on the categories
 * 
 * @author zubairov
 * 
 */
public class RSSEmitterImpl extends CommonEngineService {

	private static final ContentType RSS_CONTENT_TYPE = new ContentType(
			"text/xml");

	/**
	 * Name of the service
	 */
	private static final String SERVICE_NAME = "rss";

	/**
	 * Log
	 */
	private static final Log log = LogFactory.getLog(RSSEmitterImpl.class);

	/**
	 * Injected
	 */
	private CategoryDAO categoryDAO;
	
	/**
	 * Injected
	 */
	private EventDAO eventDAO;
	
	/**
	 * Injected
	 */
	private IEngineService externalService;

	/**
	 * {@inheritDoc}
	 */
	public String getName() {
		return SERVICE_NAME;
	}

	/**
	 * Renders output to the client
	 */
	public void service(IRequestCycle cycle) throws IOException {
		log.debug("Serving RSS/Atom feed from " + request.getRequestURI());
		OutputStream stream = response.getOutputStream(RSS_CONTENT_TYPE);
		SyndFeed feed = new SyndFeedImpl();
		feed.setFeedType("atom_1.0");
		feed.setTitle("Bookie events feed");
		feed.setLink("http://www.freebookie.org/");
		feed
				.setDescription("This feed agregates events from the freebookie.org categories");

		List<SyndEntryImpl> entries = new ArrayList<SyndEntryImpl>();
		generateEntries(entries);
		feed.setEntries(entries);
		log.debug("Sending RSS/Atom feed with " + entries.size() + " entries");
		SyndFeedOutput output = new SyndFeedOutput();
		try {
			output.output(feed, new OutputStreamWriter(stream));
		} catch (FeedException e) {
			throw new ApplicationRuntimeException(e);
		}
		log.debug("RSS/Atom feed was successfully generated");
	}

	/**
	 * Creates one entry for each event in each category
	 * 
	 * @param entries
	 */
	private void generateEntries(List<SyndEntryImpl> entries) {
		List<Category> categories = categoryDAO.findAll();
		for (Iterator iter = categories.iterator(); iter.hasNext();) {
			Category category = (Category) iter.next();
			List<Event> events = eventDAO.findByCategory(category.getId());
			for (Iterator iterator = events.iterator(); iterator.hasNext();) {
				Event event = (Event) iterator.next();
				SyndEntryImpl entry = new SyndEntryImpl();
				entry.setTitle(event.getLabel());
				entry.setAuthor("freebookie.org");
				entry.setPublishedDate(event.getEndDate());
				ILink link = externalService.getLink(false,
							new ExternalServiceParameter("EventDetails", new Object[]{event.getId()}));
				entry.setLink(link.getAbsoluteURL());
				SyndContentImpl description = new SyndContentImpl();
				description.setType("text/plain");
				description.setValue("Event " + event.getLabel()
						+ " in category " + category.getName()
						+ " deanline is " + event.getEndDate());
				entry.setDescription(description);
				entries.add(entry);
			}
		}
	}

	/**
	 * @category injector
	 * @param categoryDAO
	 */
	public void setCategoryDAO(CategoryDAO categoryDAO) {
		this.categoryDAO = categoryDAO;
	}

	
	/**
	 * Setter for EventDAO. 
	 * 
	 * @category injector
	 * @param eventDAO dao to inject
	 */
	public void setEventDAO(EventDAO eventDAO) {
		this.eventDAO = eventDAO;
	}

	/**
	 * Setter for IEngineService. 
	 * 
	 * @category injector
	 * @param externalService service to inject
	 */
	public void setExternalService(IEngineService externalService) {
		this.externalService = externalService;
	}
}
